package com.jyniubi.gmail.index.service.impl;

import com.alibaba.fastjson.JSON;
import com.jyniubi.gmail.common.bean.ResponseVo;
import com.jyniubi.gmail.index.annotation.GmailCache;
import com.jyniubi.gmail.index.feign.GmailPmsClient;
import com.jyniubi.gmail.index.service.IndexService;
import com.jyniubi.gmail.index.utils.DistributedLock;
import com.jyniubi.gmail.pms.entity.CategoryEntity;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class IndexServiceImpl implements IndexService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private GmailPmsClient pmsClient;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private DistributedLock distributedLock;

    private final static String KEY_PREFIX = "index:cates:";
    private final static String LOCK_PREFIX = "index:category:lock";
    @Override
    public List<CategoryEntity> queryLvl1Categories() {
        ResponseVo<List<CategoryEntity>> listResponseVo = this.pmsClient.queryCategoriesById(0L);
        return listResponseVo.getData();
    }

    @GmailCache(prefix = KEY_PREFIX,timeout = 129600,random = 14400,lock = LOCK_PREFIX)
    @Override
    public List<CategoryEntity> queryLvl2CategoriesWithSubs(Long cid) {
        System.out.println("========目标方法=========");
        ResponseVo<List<CategoryEntity>> categoriesResponseVo = this.pmsClient.queryLvl23CategoriesWithSubs(cid);
        return categoriesResponseVo.getData();
    }


    public List<CategoryEntity> queryLvl2CategoriesWithSubs2(Long cid) {
        // 先查询缓存，如果缓存命中则直接返回
        String json = null;
        json = this.redisTemplate.opsForValue().get(KEY_PREFIX + cid);
        if (StringUtils.isNotBlank(json)){
            return JSON.parseArray(json,CategoryEntity.class);
        }
        // 为了防止缓存击穿，加分布式锁
        RLock fairLock = this.redissonClient.getFairLock(LOCK_PREFIX + cid);
        fairLock.lock();
        try {
            // 如果在这个获取锁的过程中其他请求已经获取到该锁，并将查询数据添加到缓存中，需再次确认缓存中是否存在该数据
            // 先查询缓存，如果缓存命中则直接返回
            json = this.redisTemplate.opsForValue().get(KEY_PREFIX + cid);
            if (StringUtils.isNotBlank(json)){
                return JSON.parseArray(json,CategoryEntity.class);
            }
            // 如果缓存没有命中，则查询数据库加入缓存
            ResponseVo<List<CategoryEntity>> categoriesResponseVo = this.pmsClient.queryLvl23CategoriesWithSubs(cid);
            List<CategoryEntity> categoryEntities = categoriesResponseVo.getData();
            if (CollectionUtils.isEmpty(categoryEntities)){
                // 为了防止缓存穿透，数据即使为null也缓存，但缓存时间不超过5min
                this.redisTemplate.opsForValue().set(KEY_PREFIX + cid,JSON.toJSONString(categoryEntities),5, TimeUnit.MINUTES);
            }else {
                // 为了防止缓存击穿，给缓存时间加随机值
                this.redisTemplate.opsForValue().set(KEY_PREFIX + cid,JSON.toJSONString(categoryEntities),90 + new Random().nextInt(5), TimeUnit.DAYS);
            }
            return categoryEntities;
        } finally {
            fairLock.unlock();
        }
    }



// --------------------测试---------------------------------

    @Override
    public synchronized void testLock() {
        //获取锁
        RLock lock = this.redissonClient.getLock("lock");
        lock.lock();
        try {
            String numStr = this.redisTemplate.opsForValue().get("num");
            if (StringUtils.isBlank(numStr)){
                this.redisTemplate.opsForValue().set("num","1");
                return;
            }
            int num = Integer.parseInt(numStr);
            this.redisTemplate.opsForValue().set("num",String.valueOf(++num));
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    @Override
    public void testWrite() {
        RReadWriteLock lock = this.redissonClient.getReadWriteLock("rwl");
        lock.writeLock().lock(10,TimeUnit.SECONDS);
        System.out.println("一顿写操作");
    }

    @Override
    public void testRead() {
        RReadWriteLock lock = this.redissonClient.getReadWriteLock("rwl");
        lock.readLock().lock(10,TimeUnit.SECONDS);
        System.out.println("一顿读操作");
    }

    @Override
    public void testLatch() {
        RCountDownLatch cdl = this.redissonClient.getCountDownLatch("cdl");
        try {
            cdl.trySetCount(6);
            cdl.await();
            System.out.println("班长一顿锁门操作...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void testCountDown() {
        RCountDownLatch cdl = this.redissonClient.getCountDownLatch("cdl");
        System.out.println("同学出来了...");
        cdl.countDown();

    }

    @Override
    public void testSemaphore() {
        RSemaphore semaphore = this.redissonClient.getSemaphore("semaphore");
        int permitsToAcquire = 6;
        try {
            semaphore.acquire(permitsToAcquire);
            System.out.println("有车位了");
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }finally {
            // 确保释放所有已获取的许可
            for (int i = 0; i < permitsToAcquire; i++) {
                semaphore.release();
            }
        }
    }

    public synchronized void testLock1() {
        //获取锁
        String uuid = UUID.randomUUID().toString();
        this.distributedLock.tryLock("lock",uuid,30);
        try {
            String numStr = this.redisTemplate.opsForValue().get("num");
            if (StringUtils.isBlank(numStr)){
                this.redisTemplate.opsForValue().set("num","1");
                return;
            }
            int num = Integer.parseInt(numStr);
            this.redisTemplate.opsForValue().set("num",String.valueOf(++num));

            TimeUnit.SECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放锁
            this.distributedLock.unlock("lock",uuid);
        }
    }

    private void testsubLock(String uuid){
        this.distributedLock.tryLock("lock",uuid,30);
        System.out.println("====================");
        this.distributedLock.unlock("lock",uuid);
    }

    public synchronized void testLock2() {
        //获取锁
        String uuid = UUID.randomUUID().toString();
        Boolean lock = this.redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);
        if (!lock){
            // 获取锁失败
            try {
                Thread.sleep(50);
                this.testLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            // 给锁设置过期时间
            try {
                this.redisTemplate.expire("lock",3,TimeUnit.SECONDS);
            } finally {
                //释放锁，先判断再释放
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] " +
                        "then " +
                        "   return redis.call('del',KEYS[1]) " +
                        "else " +
                        "   return 0 " +
                        "end";
                this.redisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class), Arrays.asList("lock"),uuid);
            }

        }
        /*String numStr = this.redisTemplate.opsForValue().get("num");
        if (StringUtils.isBlank(numStr)){
            this.redisTemplate.opsForValue().set("num","1");
            return;
        }
        int num = Integer.parseInt(numStr);
        this.redisTemplate.opsForValue().set("num",String.valueOf(++num));*/
    }
}
